package ltd.hxya.novel.user.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.jsonwebtoken.JwtBuilder;
import ltd.hxya.novel.common.bean.Result;
import ltd.hxya.novel.common.bean.ResultEnum;
import ltd.hxya.novel.common.bean.UserBean;
import ltd.hxya.novel.common.constant.BaseConstant;
import ltd.hxya.novel.common.exception.NovelGlobalException;
import ltd.hxya.novel.common.utils.*;
import ltd.hxya.novel.common.vo.PageParam;
import ltd.hxya.novel.user.entity.User;
import ltd.hxya.novel.user.entity.dto.*;
import ltd.hxya.novel.user.interceptor.UserLoginInterceptor;
import ltd.hxya.novel.user.mapper.UserMapper;
import ltd.hxya.novel.user.service.IUserService;
import ltd.hxya.novel.user.utils.auth.BusinessException;
import ltd.hxya.novel.user.utils.constant.CacheConsts;
import ltd.hxya.novel.user.utils.constant.ErrorCodeEnum;
import ltd.hxya.novel.user.utils.image.ImgVerifyCodeUtils;
import ltd.hxya.novel.user.utils.rest.RestResp;
import ltd.hxya.novel.user.vo.UserInfoVo;
import ltd.hxya.novel.user.vo.UserVo;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import javax.imageio.ImageIO;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author hxya
 * @since 2022-09-13
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private StringRedisTemplate redisTemplate;




    @Override
    public Map<String, String> verifyCode() throws IOException {
        Object[] objs = VerificationCodeUtil.createImage();
        Map<String,String> imgMap = new HashMap<>();
        //将图片输出给浏览器
        BufferedImage image = (BufferedImage) objs[1];
        String value = objs[0].toString().toLowerCase();
        String key = UUID.randomUUID().toString();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        // 使用ServletOutputStream流可直接输出图片
//        ServletOutputStream outputStream = response.getOutputStream();
        ImageIO.write(image, "png", outputStream);
        Base64 base64 = new Base64();
        String img = new String(base64.encode(outputStream.toByteArray()));
        //将验证码保存到redis中
        imgMap.put("img",img);
        imgMap.put("key",key);
        redisAddcode(key,value);

        return imgMap;
    }


    @Override
    public UserInfoVo userInfo() {
        UserBean userBean = UserLoginInterceptor.threadLocal.get();
        String id = userBean.getId();
        User user = userInfo(id);
        UserInfoVo userInfoVo = new UserInfoVo();
        BeanUtils.copyProperties(user,userInfoVo);
        return userInfoVo;
    }

    @Override
    public Page<User> getUserList(User user) {

        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        if (!StringUtils.isEmpty(user.getUsername())){
            userQueryWrapper.eq("username",user.getUsername());
        }
        if (!StringUtils.isEmpty(user.getNickName())){
            userQueryWrapper.eq("nick_name",user.getNickName());
        }
        if (!StringUtils.isEmpty(user.getStatus())){
            userQueryWrapper.eq("status",user.getStatus());
        }
        if (user.getParams()!=null){
            String beginTime = (String) user.getParams().get("beginTime");
            String endTime = (String) user.getParams().get("endTime");
            if (!StringUtils.isEmpty(beginTime)){
                userQueryWrapper.ge("create_time",beginTime);
            }
            if (!StringUtils.isEmpty(endTime)){
                userQueryWrapper.le("create_time",endTime);
            }


        }
        //获取相关分页信息
        PageParam pageParam = PageUtils.getPageParams();
        Page<User> page = this.page(new Page<User>(pageParam.getCurrent(), pageParam.getSize()), userQueryWrapper);
        return page;
    }

    @Override
    public User userInfo(String userId) {
        User user = this.getById(userId);
        return user;
    }

    @Override
    public void changeStatus(User user) {
        UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
        userUpdateWrapper.eq("id",user.getId()).set("status",user.getStatus());
        this.update(userUpdateWrapper);
    }

    @Override
    public void addUser(User user) {
        //判断数据库中是否有该用户存在，如果有，则返回错误信息，如果没有，则添加该角色
        String username = user.getUsername();
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("username",username);
        User user1 = baseMapper.selectOne(userQueryWrapper);
        if (user1!=null){
            throw new NovelGlobalException(ResultEnum.USER_IS_EXIST.getCode(), ResultEnum.USER_IS_EXIST.getMsg());
        }
        user.setId(IdWorker.getIdStr());
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        baseMapper.insert(user);
    }

    @Override
    public void updateUser(User user) {
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        User user1 = baseMapper.selectById(user.getId());
        if (user1==null){
            throw new NovelGlobalException(ResultEnum.USER_IS_DELETE.getCode(), ResultEnum.USER_IS_DELETE.getMsg());
        }
        String rawPassword = user.getPassword();
        if (StringUtils.isEmpty(rawPassword)){
            rawPassword = BaseConstant.DEFAULT_USER_PASSWORD;
        }
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode(rawPassword);
        user.setPassword(password);
        user.setUpdateTime(LocalDateTime.now());
        baseMapper.updateById(user);
    }

    @Override
    public void updatePassword( String id,  String rawPassword) {
        UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode(rawPassword);
        userUpdateWrapper.eq("id",id).set("password",password);
        update(userUpdateWrapper);
    }

    /**
     * 获取图片验证码接口
     * 把图片存在了redis中，并且设置了时间
     * @return
     * @throws IOException
     */
    @Override
    public Result<ImgVerifyCodeRespDto> getImgVerifyCode() {
        String uuid = IdWorker.get32UUID();
        ImgVerifyCodeRespDto build = ImgVerifyCodeRespDto.builder()
                .sessionId(uuid)
                .img(getImgVerifyCode(uuid))
                .build();
        return Result.success(build);
    }

    /**
     * 用户注册的处理
     * @param dto 注册参数
     * @return
     */
    @Override
    public Result<UserRegisterRespDto> register(UserRegisterReqDto dto) {
        // 1、校验图形验证码是否正确（redis中的验证码，和页面输入的进行比较处理）
        String s = redisTemplate.opsForValue().get(CacheConsts.IMG_VERIFY_CODE_CACHE_KEY + dto.getSessionId());
        if (!s.equals(dto.getVelCode())) {
            // 图形验证码校验失败
            throw new BusinessException(ErrorCodeEnum.USER_VERIFY_CODE_ERROR);
        }

        // 2、查询数据库，看看账号(手机号)是否被注册过
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username",dto.getUsername()).last("limit 1");
        if (baseMapper.selectCount(queryWrapper)>0){
            throw new BusinessException(ErrorCodeEnum.USER_NAME_EXIST);
        }

        // 3、注册成功，保存用户信息
        User user = new User();
        user.setPassword(DigestUtils.md5DigestAsHex(dto.getPassword().getBytes(StandardCharsets.UTF_8)));
        user.setUsername(dto.getUsername());
        user.setNickName(dto.getUsername());
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        user.setId(UUID.randomUUID().toString());
        baseMapper.insert(user);

        // 4、删除验证码
        redisTemplate.delete(CacheConsts.IMG_VERIFY_CODE_CACHE_KEY  + dto.getSessionId());

        // 5、生成token
        String s1 = JSON.toJSONString(user);
        String token = JwtUtils.getToken(s1);
        return Result.success(
                UserRegisterRespDto.builder()
                        .token(token)
                        .uid(user.getId())
                        .build()
        );
    }


    @Override
    public Result<UserLoginRespDto> login(UserLoginReqDto dto) {
        // 1、查询用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username",dto.getUsername())
                .last("limit 1");
        User user = baseMapper.selectOne(queryWrapper);
        if (Objects.isNull(user)){
            // 2、不存在，返回不存在
            throw new NovelGlobalException(ResultEnum.NOT_REGISTER);
        }
        // 3、存在查询密码，进行密码的加密处理
        String s = DigestUtils.md5DigestAsHex(dto.getPassword().getBytes(StandardCharsets.UTF_8));
        if (!user.getPassword().equals(s)){
            // 密码不相等
            throw new NovelGlobalException(ResultEnum.PASSWORD_ERROR);
        }
        // 4、登录成功，返回jwt的处理
        // 生成token的处理
        String s1 = JSON.toJSONString(user);
        String token = JwtUtils.getToken(s1);
        return Result.success(UserLoginRespDto.builder()
                .token(token)
                .uid(user.getId())
                .nickName(user.getNickName())
                .build());
    }

    @Override
    public Result<UserInfoRespDto> getUserInfo(String id) {
        User user = baseMapper.selectById(id);
        return Result.success(UserInfoRespDto.builder()
                .nickName(user.getNickName())
                .userSex(user.getUserSex())
                .userPhoto(user.getUserPhoto())
                .build());
    }

    @Override
    public Result<Void> updateUserInfo(UserInfoUptReqDto dto) {
        User user = new User();
        user.setId(dto.getUserId());
        user.setNickName(dto.getNickName());
        user.setUserPhoto(dto.getUserPhoto());
        user.setUserSex(dto.getUserSex());
        baseMapper.updateById(user);
        return Result.success();
    }


    /**
     * 生成图片，并且存到redis中，redis中的key就是
     * @param sessionId
     * @return
     */
    public String getImgVerifyCode(String sessionId){
        String randomVerifyCode = ImgVerifyCodeUtils.getRandomVerifyCode(4);
        String img = null;
        try {
            img = ImgVerifyCodeUtils.genVerifyCodeImg(randomVerifyCode);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 存到redis中，然后时间要设置成5min
        redisTemplate.opsForValue().set(CacheConsts.IMG_VERIFY_CODE_CACHE_KEY + sessionId,
                randomVerifyCode, Duration.ofMinutes(5));
        return img;
    }


    private void redisAddcode(String key, String value) {
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        //将验证码添加到redis
        valueOperations.set(key,value,10,TimeUnit.MINUTES);

    }

    private String jwtToken( User user,Map<String,Object> objectMap) {
        UserBean userBean = new UserBean();
        userBean.setUsername(user.getUsername());
        userBean.setId(user.getId());
        userBean.setNickName(user.getNickName());
        objectMap.put("user",userBean);
        String userJson = JSON.toJSONString(userBean);
        String token = JwtUtils.getToken(userJson);
        objectMap.put("token",token);
        return token;
    }
}
